Skip to content

[infra] Backend integration tests — testcontainers 取代 docker-compose postgres#293

Merged
nurockplayer merged 11 commits intodevelopfrom
infra/testcontainers-integration-tests
Apr 20, 2026
Merged

[infra] Backend integration tests — testcontainers 取代 docker-compose postgres#293
nurockplayer merged 11 commits intodevelopfrom
infra/testcontainers-integration-tests

Conversation

@nurockplayer
Copy link
Copy Markdown
Owner

@nurockplayer nurockplayer commented Apr 19, 2026

Summary

用 testcontainers-go 取代 CI 中的 docker compose up -d --wait postgres,讓 integration tests 完全自包含。

改動

  • backend/internal/services/testutil_pg_test.go:加入 TestMain + testcontainers,整個 test suite 共用一個 PostgreSQL 容器(UUID schema 隔離依然保留)
  • backend/go.mod / go.sum:加入 testcontainers-go v0.42.0
  • .github/workflows/ci.yml:backend-integration job 改為直接在 runner 上 go test -tags integration,移除 artifact download、docker load、docker compose up/down 步驟

為什麼

目前 CI backend-integration job 需要先 docker compose up -d --wait postgres,再 docker compose run app go test。testcontainers 讓測試程式碼自己管理 DB 容器,CI 更簡單,本地也不需要手動 docker compose up 就能跑 integration tests。

Scope 對齊

Source of truth: #292 — Backend integration tests — 用 testcontainers 加速 DB 初始化

Depends on PR: #288

Backend contract already in develop:

  • yes
  • no(infra 改動,無 backend contract)

本 PR 是否完全在 source of truth 範圍內?

  • 否,已另開 issue / PR 處理超出部分

本 PR 明確不做

  • 不改變測試邏輯或測試覆蓋範圍
  • 不改變 schema isolation 機制(UUID schema 依然保留)
  • 不修改其他 CI jobs

refs #292

Summary by CodeRabbit

发布说明

  • 重构

    • 优化了 CI/CD 流程管理和测试基础设施,提升系统稳定性和效率。
  • 依赖更新

    • 更新了后端依赖库版本,增强系统兼容性和性能。

nurockplayer and others added 8 commits April 19, 2026 17:35
Create independent backend-build job that builds image once, then backend
and backend-integration jobs download the artifact and run tests in parallel.

This reduces total CI time from (build + unit + integration) to
(build + max(unit, integration)).

Changes:
- New backend-build job: build → docker save → upload artifact
- backend job: download artifact → docker load → run unit tests
- backend-integration job: download artifact → docker load → run integration tests
- Both test jobs now depend on backend-build (parallel execution)
- notify-discord updated to include backend-build in needs

refs #285

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Ensure docker save command failure fails fast, preventing upload of
corrupted artifacts. Add explicit file size validation.

refs #285
Ensure backend image build failures are reported as CI failures, not
skipped jobs, so PR merge is properly blocked by required checks.

The backend-ci gate depends on backend-build, backend, and
backend-integration. If backend-build fails, the gate fails, guaranteeing
required checks will block merge.

Update pr-scope-policy.md to reflect the new gate check name.

refs #285
Only fail when backend-build, backend, or backend-integration actually
fail. Allow them to be skipped due to scope-gate rules.

refs #285
Use allowlist mode: only success or skipped are acceptable.
Prevents branch protection bypass when jobs are manually cancelled.

refs #285
Cannot dynamically expand needs.$job.result via expressions.
Use env variables to pass values, then iterate in shell.

Fixes: #288 (comment)

refs #285
Replace docker-compose postgres dependency with testcontainers-go.
Tests now self-manage PostgreSQL lifecycle via TestMain, removing
the need for external DB setup in CI.

refs #292

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Base automatically changed from infra/backend-test-parallelization to develop April 19, 2026 17:50
…ontainers)

refs #292

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 19, 2026

Warning

Rate limit exceeded

@nurockplayer has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 39 minutes and 39 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 39 minutes and 39 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 79158411-90cf-403b-8857-afa86977c4de

📥 Commits

Reviewing files that changed from the base of the PR and between 2b06404 and 5702493.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

整體概況

此變更將後端整合測試的執行方式從 Docker Compose 改為主機端 Go 測試,並利用 testcontainers 動態啟動 PostgreSQL 容器,移除對預構建後端映像的依賴。

變更一覽

Cohort / File(s) 摘要
CI 工作流程調整
.github/workflows/ci.yml, scripts/check-backend-ci-cache.sh
移除 backend-integration 工作對 backend-build 的依賴,改為主機上執行 go test -tags integration 而非 Docker Compose,移除相關快取檢查。
Testcontainers 整合
backend/internal/services/testutil_pg_test.go
新增 TestMain 邏輯,在測試前動態啟動 PostgreSQL 16-alpine testcontainer,或使用 DATABASE_URL 環境變數(若已設置),並更新 newPGTestDB 取用容器連線字串。
依賴版本更新
backend/go.mod
新增 testcontainers-go 及其 PostgreSQL 模組、Docker/containerd 相關模組與 OpenTelemetry 工具庫,更新 OpenTelemetry 至 v1.41.0,移除過時的 tklauser 模組。

關鍵風險評估

⚠️ 主要發現

  1. CI 環境 Docker 可用性風險 (backend/go.mod, .github/workflows/ci.yml)

    • 新工作流程依賴 testcontainers 在 CI 環境中啟動 PostgreSQL 容器,需要 Docker 守護程序可用
    • 若 CI 環境無法提供 Docker 或組態不同,整合測試將失敗
    • 建議確認 CI runners 已預先配置 Docker 支援,並驗證 testcontainers 預配置(如 Docker socket 路徑)符合實際環境
  2. 測試隔離與容器生命週期 (backend/internal/services/testutil_pg_test.go)

    • pgContainerURL 為全域變數,TestMain 中容器啟動後才初始化
    • 若測試並行執行或有多個測試檔案導入此包,容器只會啟動一次(預期行為),但需確保測試間資料隔離(如 schema 隔離)實際有效
    • 建議新增測試驗證跨多個測試的資料隔離正確性
  3. go.mod 依賴變更複雜度 (backend/go.mod)

    • 新增 44 行間接依賴(含 testcontainers、Docker、containerd 相關模組),移除 6 行
    • 間接依賴版本差異多,需確認無版本衝突或相容性問題
    • 建議執行 go mod tidy && go mod verify 驗證完整性

邏輯合理性

  • DATABASE_URL 環境變數優先級設計合理,允許本地開發與 CI 環境差異化
  • pgContainerURL 空值檢查邏輯正確,跳過測試而非崩潰
  • testcontainer 啟動條件清晰(無 DATABASE_URL 時才啟動)

估計審查工作量

🎯 3 (中等複雜度) | ⏱️ 約 20 分鐘

可能相關議題

  • #292:直接實現 testcontainers 用於 DB 背景整合測試,與此變更目標一致

可能相關 PRs

  • #288:同樣修改 backend-integration 工作流程依賴與執行步驟,但方向相反(本 PR 移除映像依賴,該 PR 添加映像依賴)
  • #274:修改 backend-integration 工作的依賴與執行條件
  • #221:修改後端整合工作的測試執行方式(本 PR 從 Docker 改為主機 go test

建議審查者

  • Erick52106
  • k49778749-stack

🐳 從容器到主機,
測試自動起伏——
Testcontainers 輕盈舞,
無須預建映像,
CI 管道更暢快 ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 標題清楚說明主要改動:使用 testcontainers 取代 docker-compose 中的 PostgreSQL 管理,這正是本次 PR 的核心目的。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch infra/testcontainers-integration-tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 19, 2026

PR Scope Police

  • PASS: scope checks passed.

Snapshot

  • Mode: standard PR
  • Docs/template/metadata only: no
  • Changed files: 5
  • Diff lines (+/-): 218
  • Product surfaces: backend
  • Dependency blocked: no
  • Auto-close triggered: no
  • Auto-close label: scope-violation
  • Dependency block label: blocked-by-dependency
  • Bypass label: scope-exception

@github-actions github-actions Bot added the needs-codex-review New commits pushed after CHANGES_REQUESTED — pending Codex review label Apr 19, 2026
Integration tests now run natively via testcontainers (setup-go),
not inside the app Docker container. The two require_in checks for
docker compose run integration build/test no longer apply.

refs #293

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/internal/services/testutil_pg_test.go (1)

71-100: ⚠️ Potential issue | 🟠 Major

cleanup 位置錯誤會導致 schema 洩漏,且缺少連線池關閉。

newPGTestDB 在第 96 行註冊 cleanup,但在此之前有多個失敗路徑(第 76、81、89、93 行的 Fatalf)會跳過 cleanup 執行。若 testDB.Open()migratePGTestDB() 失敗,建立的 schema 將永久存留;同時 adminDB 的連線也會洩漏。此外,即使成功路徑,cleanup 也未關閉 adminDBtestDB 的連線池,在 shared PostgreSQL container 下會不斷累積 idle connections。

修正方式:將 cleanup 移到 schema 建立之後立即註冊,並在 cleanup 中呼叫 testDB.DB().Close()adminDB.DB().Close()

修正方向
	adminDB, err := gorm.Open(postgres.Open(pgContainerURL), &gorm.Config{
		Logger:         logger.Default.LogMode(logger.Silent),
		TranslateError: true,
	})
	if err != nil {
		t.Fatalf("open postgres admin db: %v", err)
	}

	schemaName := "test_" + strings.ReplaceAll(uuid.NewString(), "-", "")
	if err := adminDB.Exec(fmt.Sprintf(`CREATE SCHEMA IF NOT EXISTS "%s"`, schemaName)).Error; err != nil {
		t.Fatalf("create schema %s: %v", schemaName, err)
	}

+	var testDB *gorm.DB
+	t.Cleanup(func() {
+		if testDB != nil {
+			if sqlDB, err := testDB.DB(); err == nil {
+				_ = sqlDB.Close()
+			}
+		}
+		if err := adminDB.Exec(fmt.Sprintf(`DROP SCHEMA IF EXISTS "%s" CASCADE`, schemaName)).Error; err != nil {
+			t.Errorf("drop schema %s: %v", schemaName, err)
+		}
+		if sqlDB, err := adminDB.DB(); err == nil {
+			_ = sqlDB.Close()
+		}
+	})
+
-	testDB, err := gorm.Open(postgres.Open(withSearchPath(pgContainerURL, schemaName)), &gorm.Config{
+	testDB, err = gorm.Open(postgres.Open(withSearchPath(pgContainerURL, schemaName)), &gorm.Config{
		Logger:         logger.Default.LogMode(logger.Silent),
		TranslateError: true,
	})
	if err != nil {
		t.Fatalf("open postgres test db: %v", err)
	}

	if err := migratePGTestDB(testDB); err != nil {
		t.Fatalf("migrate postgres test db: %v", err)
	}

-	t.Cleanup(func() {
-		if err := adminDB.Exec(fmt.Sprintf(`DROP SCHEMA IF EXISTS "%s" CASCADE`, schemaName)).Error; err != nil {
-			t.Fatalf("drop schema %s: %v", schemaName, err)
-		}
-	})

	return testDB
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/internal/services/testutil_pg_test.go` around lines 71 - 100, The
cleanup is registered too late so failures after schema creation can leak the
schema and DB connections; move the t.Cleanup registration to immediately after
creating the schema (right after the adminDB.Exec CREATE SCHEMA success) and
make the cleanup close both connection pools and drop the schema: call
adminDB.DB().Close() and testDB.DB().Close() (or the gorm DB().Close
equivalents) and execute DROP SCHEMA IF EXISTS "<schemaName>" CASCADE inside
that cleanup so adminDB and testDB are always closed and the schema always
removed even if migratePGTestDB or subsequent opens fail; update references in
this function (adminDB, testDB, migratePGTestDB, withSearchPath) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 221-224: The workflow uses older JavaScript action versions that
don't support Node 24; update the action references: change the actions/setup-go
usage (symbol "uses: actions/setup-go@v4") to use v6 and change the
actions/checkout usage (symbol "uses: actions/checkout@v4") to v5 or v6 so both
actions run on Node 24-compatible runners; after updating, verify the workflow
passes actionlint and that the runner version meets the actions' minimum (runner
v2.327.1+ for setup-go v6).

---

Outside diff comments:
In `@backend/internal/services/testutil_pg_test.go`:
- Around line 71-100: The cleanup is registered too late so failures after
schema creation can leak the schema and DB connections; move the t.Cleanup
registration to immediately after creating the schema (right after the
adminDB.Exec CREATE SCHEMA success) and make the cleanup close both connection
pools and drop the schema: call adminDB.DB().Close() and testDB.DB().Close() (or
the gorm DB().Close equivalents) and execute DROP SCHEMA IF EXISTS
"<schemaName>" CASCADE inside that cleanup so adminDB and testDB are always
closed and the schema always removed even if migratePGTestDB or subsequent opens
fail; update references in this function (adminDB, testDB, migratePGTestDB,
withSearchPath) accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f547e244-ba8f-406e-926c-5cff9d3d55d2

📥 Commits

Reviewing files that changed from the base of the PR and between 88f7c4c and 2b06404.

⛔ Files ignored due to path filters (1)
  • backend/go.sum is excluded by !**/*.sum
📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • backend/go.mod
  • backend/internal/services/testutil_pg_test.go
  • scripts/check-backend-ci-cache.sh
💤 Files with no reviewable changes (1)
  • scripts/check-backend-ci-cache.sh

Comment thread .github/workflows/ci.yml
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 requires Node 24-compatible actions.

refs #293

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@nurockplayer nurockplayer merged commit 32a9dbc into develop Apr 20, 2026
15 checks passed
@nurockplayer nurockplayer deleted the infra/testcontainers-integration-tests branch April 20, 2026 07:36
nurockplayer added a commit that referenced this pull request Apr 21, 2026
* chore(deps): bump the deps group in /tachimint with 4 updates

Bumps the deps group in /tachimint with 4 updates: [axios](https://github.com/axios/axios), [i18next](https://github.com/i18next/i18next), [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `axios` from 1.14.0 to 1.15.0
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.14.0...v1.15.0)

Updates `i18next` from 26.0.3 to 26.0.4
- [Release notes](https://github.com/i18next/i18next/releases)
- [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md)
- [Commits](https://github.com/i18next/i18next/compare/v26.0.3...v26.0.4)

Updates `@types/node` from 25.5.2 to 25.6.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `typescript-eslint` from 8.58.0 to 8.58.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.58.1/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: deps
- dependency-name: i18next
  dependency-version: 26.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: deps
- dependency-name: "@types/node"
  dependency-version: 25.6.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: deps
- dependency-name: typescript-eslint
  dependency-version: 8.58.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: deps
...

Signed-off-by: dependabot[bot] <support@github.com>

* feat(contracts): 實作 TachiToken Soulbound ERC-20

refs #110

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* test(contracts): 新增 TachiToken Foundry 單元測試

closes #110

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* chore(contracts): 補回 foundry.toml(誤刪修復)

contracts/foundry.toml 原由 #125(#128)建立,因 Codex 執行時
誤刪導致本 branch 工作目錄缺少此檔。內容與 420e012 完全相同,
不修改任何設定,僅確保 forge build / forge test 可正常執行。

refs #110

* docs: add tachimint Figma design prompts

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* fix(contracts): 補充 MAX_SUPPLY 單位註釋(wei)

review 回應:
- 項目 3:補 MAX_SUPPLY wei 單位說明
- 項目 2(burn 空實作):無需修改,_burn(from, amount) 於 L22 已存在,
  reviewer 係從摘要判斷,非實際查閱原始碼
- 項目 4 邊界測試:test_mint_exceedsCap_reverts 已覆蓋
  「mint 至 MAX_SUPPLY 後再 +1」的精確邊界,無需新增

refs #110

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* test(contracts): 補零地址、burn 零金額、approve 測試案例

review 回應(項目 1、2、4):
- 項目 1(零地址 mint):OZ v5 _mint() 已內建 ERC20InvalidReceiver revert,
  合約層不加 require(避免冗餘),補測試記錄此行為
- 項目 2(burn 空實作):reviewer 誤判,_burn(from, amount) 於 L22 已存在,
  無需修改
- 項目 4 測試缺口:
  - 零地址測試 → 補上
  - 邊界測試 → test_mint_exceedsCap_reverts 已覆蓋,無需新增
  - burn 零金額 → 補上
  - approve 行為 → 補上

refs #110

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* test(contracts): 鎖定 expectRevert 具體錯誤訊息

將 3 處 vm.expectRevert() 改為明確指定錯誤字串:
- test_mint_exceedsCap_reverts → bytes("TachiToken: cap exceeded")
- test_transfer_reverts → bytes("TachiToken: soulbound")
- test_transferFrom_reverts → bytes("TachiToken: soulbound")

L27(onlyOwner)與 L67(零地址)使用 OZ custom error,
不適用 bytes() 形式,維持 vm.expectRevert()。

refs #110

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* test(contracts): 補 burn 非 owner 負向測試

新增 test_burn_nonOwner_reverts,確保 burn() 權限回歸可被攔截。
vm.expectRevert() 維持裸形式(OZ OwnableUnauthorizedAccount
custom error,不適用 bytes() 鎖定),與 test_mint_nonOwner_reverts 一致。

refs #110

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* chore: trigger CI re-run after scope police body fix

refs #110

* feat(contracts): override approve 封鎖 Soulbound allowance 路徑

approve 保留可用會製造「已授權但永遠無法轉移」的假象,前端走標準
ERC-20 approve flow 時會被誤導並浪費 gas。Phase 2 平台 Router
直接走 burn/mint,不需要 approval 路徑,一併封鎖語意才完整。

OZ v5 已移除 increaseAllowance / decreaseAllowance,
僅需 override approve 一個函數。

refs #110

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* test(contracts): 更新測試反映 approve 封鎖語意

- test_approve_succeeds_even_though_transfer_blocked
  → test_approve_reverts(approve 現在應 revert)
- test_transferFrom_reverts 移除 approve 前置步驟
  (approve 已封鎖,無法作為 allowance 設定手段)

refs #110

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* chore: relax scope police for dependabot PRs

* [frontend] Extension Demo — 音效系統(Web Audio Bridge) (#171)

* feat: extension demo 音效系統

- 新增 useSound hook(Web Audio API 合成,無外部音效檔)
- 新增 content script audio bridge,讓 Discord/OBS 能捕捉 tab audio
- 挖礦點擊:5 層合成(impact transient、metallic overtone、bandpass noise、low thump、sparkle),3 variant(light 20%/normal 65%/critical 15%)隨機觸發
- 進度條完成:FF 勝利號角音符序列
- 點擊配額用完:雙聲綜藝答錯蜂鳴器
- SW 切換:短促 sine 音效
- 背景音樂:原創 G 大調 8-bit 冒險主題,獨立 ♪ ON/OFF 按鈕控制
- manifest.json 新增 content_scripts + tabs permission
- 安裝 @types/chrome、@types/react、@types/react-dom,新增 tsconfig.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: 移除 pnpm-lock.yaml,加入 .gitignore

refs #170

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: 修正 extension sound effect 型別設定

refs #170

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: 修正 extension sound effect bridge 行為

refs #170

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: 修正 extension sound effect UI 細節

refs #170

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: 修正 extension sound effect 測試敘述

refs #170

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: clarify dependabot scope police behavior

refs #177

Co-Authored-By: Codex <codex[bot]@openai.com>

* feat(backend): 載入 contract env 並補 Sepolia 部署文件

- config.go 新增 ContractConfig,載入 TACHI_CONTRACT_ADDRESS / SEPOLIA_SIGNER_KEY
- backend/.env.example 補上兩個 env var 的佔位與安全說明
- deployments/sepolia.json 新增 ownerStrategy 記錄 MVP owner 策略

refs #175

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* docs(backend): 修正 .env.example 過時註解並補 SEPOLIA_SIGNER_KEY 說明

- 移除「尚未載入」的過時 Verification note,改為指向 config.go 載入路徑
- 補充 SEPOLIA_SIGNER_KEY 的用途、管理方式與安全警示

refs #175

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* [contract] Soulbound ERC-20 Sepolia 部署 + Etherscan 驗證 (#174)

* feat(contracts): 建立 Sepolia 部署 script 與環境設定

refs #111

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* chore(contracts): 記錄 Sepolia 部署資訊與合約地址

closes #111

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* revert: 移除 backend/.env.example 改動(跨 surface)

backend/.env.example 的 TACHI_CONTRACT_ADDRESS 調整應另開 backend ticket 處理。

refs #111

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* revert: 完全移除 backend/.env.example 改動

contracts surface 不可修改 backend/,TACHI_CONTRACT_ADDRESS 另開 ticket 處理。

refs #111

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* docs(contracts): README 補 source .env 載入步驟

refs #111

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* chore(deps-dev): bump vite in /tachimint in the vite group (#164)

Bumps the vite group in /tachimint with 1 update: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).


Updates `vite` from 8.0.5 to 8.0.8
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.8/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 8.0.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: vite
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(backend): use empty placeholder for SEPOLIA_SIGNER_KEY in .env.example

refs #175

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: 新增 TachiToken contract ABI binding

refs #112

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* fix: 修正 nonce 競態、TOCTOU 及 ethclient 無條件啟動

- TachiToken: sync.Mutex 序列化 nonce 分配
- ClaimService: DB transaction 內先鎖行再 mint
- main.go: 合約設定為空時跳過 ethclient dial

refs #112

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* [frontend] Extension Demo — 音效系統 + Claim 提領介面 + HUD 金幣按鈕 + 洞穴背景 (#181)

* feat: extension demo 音效系統

- 新增 useSound hook(Web Audio API 合成,無外部音效檔)
- 新增 content script audio bridge,讓 Discord/OBS 能捕捉 tab audio
- 挖礦點擊:5 層合成(impact transient、metallic overtone、bandpass noise、low thump、sparkle),3 variant(light 20%/normal 65%/critical 15%)隨機觸發
- 進度條完成:FF 勝利號角音符序列
- 點擊配額用完:雙聲綜藝答錯蜂鳴器
- SW 切換:短促 sine 音效
- 背景音樂:原創 G 大調 8-bit 冒險主題,獨立 ♪ ON/OFF 按鈕控制
- manifest.json 新增 content_scripts + tabs permission
- 安裝 @types/chrome、@types/react、@types/react-dom,新增 tsconfig.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: 移除 pnpm-lock.yaml,加入 .gitignore

refs #170

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: 修正 extension sound effect 型別設定

refs #170

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: 修正 extension sound effect bridge 行為

refs #170

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: 修正 extension sound effect UI 細節

refs #170

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: 修正 extension sound effect 測試敘述

refs #170

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: 修正 extension sound effect 測試文案

refs #170

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: Claim 提領介面 + HUD 金幣按鈕 + 洞穴背景

- 新增 ClaimPanel(CPC→TCG 1:0.1 swap,MAX 按鈕、2s 自動 reset)
- DemoScreen 加入 'claim';App.tsx 新增 tcgBalance + handleClaim
- i18n claim.* keys(en / zh-TW / zh-CN)
- HUD CLAIM 按鈕改為 8 幀像素風旋轉金幣(正圓輪廓,crispEdges)
- useSound 新增 playClaimSound(3 層合成,~220ms)
- ClaimPanel handleClaim 觸發音效
- HUD 背景:SVG 岩壁 / 金礦石 / 水晶 + CSS 多層漸層洞穴氛圍

refs #180

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: restore sidepanel HUD bridge warning refs #180

* fix: address PR review issues refs #180

* chore: rerun PR checks refs #180

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: 限制 mint RPC context 並共用合約實例

refs #182

Co-Authored-By: Codex <codex[bot]@openai.com>

* fix: 修正 claim mint 一致性 review 意見

refs #182

Co-Authored-By: Codex <codex[bot]@openai.com>

* feat(backend): internal tachiya points query endpoint

- GET /api/v1/internal/tachiya/users/points/balance?email=xxx
- X-Tachiya-Internal-Secret header auth middleware
- config reads TACHIYA_INTERNAL_SHARED_SECRET

refs #183

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(backend): fix go.sum missing fsnotify entry for go-ethereum

refs #112

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: upgrade pgx dependency

refs #112

Co-Authored-By: Codex <codex[bot]@openai.com>

* fix: harden internal points endpoint

refs #183

Co-Authored-By: Codex <codex[bot]@openai.com>

* chore: add .go-build-cache/ to .gitignore

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: cover internal route auth registration

refs #183

Co-Authored-By: Codex <codex[bot]@openai.com>

* docs: add SpendService design spec for #186

refs #186

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: add SpendService implementation plan

refs #186

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add TachiToken.Burn() method

refs #186

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: add SpendService.Redeem unit tests (failing)

refs #186

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: implement SpendService.Redeem with reserve-then-burn flow

refs #186

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: check RowsAffected in rollbackSpendReservation

refs #186

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add SpendHandler POST /spend/redeem

refs #186

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: add binding tag to redeemRequest.Amount, document ErrSpendContractConfig fallthrough

refs #186

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: wire SpendService and POST /api/v1/spend/redeem route

closes #186

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: preserve txHash when WaitMined fails to avoid DB/chain inconsistency

- TachiToken.Burn: return txHash on WaitMined failure so callers can
  distinguish "tx broadcast but receipt unknown" from "tx never sent"
- SpendService.Redeem: only rollback DB when txHash is empty (tx not sent);
  skip rollback when txHash is non-empty to prevent chain-burned / DB-restored inconsistency
- Add TestRedeem_BurnBroadcastedButReceiptUnknown to cover new behavior
- Update spec and plan docs (status, main.go path, flow description, MD040)

refs #186

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: apply SQLite pragmas via DSN in concurrent airdrop test

PRAGMA statements executed after Open only affect the single connection
that runs them. With SetMaxOpenConns(8), other pool connections lacked
busy_timeout/WAL mode and immediately failed with "database is locked".
Moving the settings to the DSN ensures every pool connection gets them.

refs #70

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: wire tachimint claim api

refs #190

Co-Authored-By: Codex <codex[bot]@openai.com>

* [frontend] Extension demo — coupon shop UI (#188)

* feat: add extension coupon shop UI

refs #15

Co-Authored-By: Codex <codex[bot]@openai.com>

* feat: polish extension coupon shop UI

refs #15

Co-Authored-By: Codex <codex[bot]@openai.com>

* feat: tune extension claim and coupon colors

refs #15

Co-Authored-By: Codex <codex[bot]@openai.com>

* fix: coupon shop atomic redeem, lifted state, i18n items

- Persist tcgBalance and redeemedCouponIds in demo state; sync refs for sync redeem path
- handleCouponRedeem returns success / insufficient / already_redeemed
- CouponShopPanel uses i18n for catalog copy and back label; redeem button guards with isRedeeming
- Extend App coupon tests for success and duplicate paths

refs #15

Made-with: Cursor

* chore: add PR #188 body file and gh helper for scope police

refs #15

Made-with: Cursor

* fix: move PR #188 GitHub body out of plans/

plans/ is for implementation plans; PR description text belongs under .github/.

refs #15

Made-with: Cursor

---------

Co-authored-by: Codex <codex[bot]@openai.com>

* fix: localize tachimint claim copy

refs #190

Co-Authored-By: Codex <codex[bot]@openai.com>

* fix: convert TACHI amounts to raw token units

refs #192

Co-Authored-By: Codex <codex[bot]@openai.com>

* chore: add demo wallet binding helper

refs #191

Co-Authored-By: Codex <codex[bot]@openai.com>

* chore: add AGENTS.md for Codex PR review optimization (#179)

* docs: update PR review strategy

Co-Authored-By: Codex <codex[bot]@openai.com>

* chore: harden GitHub Actions for production readiness

- Expand integration tests to run all packages (./...) instead of a single test case
- Add dashboard/ npm tracking to Dependabot
- Enable Dependabot security updates via gh api

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: update AI collaboration guidelines to reduce Codex delegation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: add [infra] and [chore] PR title prefixes to scope police

- Allow [infra] and [chore] as valid PR title prefixes
- Exempt [infra] and [chore] PRs from backend contract checks
- Bootstrapping fix: [infra] PRs would be blocked by the old rules

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: notify Discord only on CI failure

Replace always-on GitHub webhook with a GitHub Actions notify job
that fires only when CI fails. Disables the noisy GitHub webhook.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: upgrade axios to 1.15.0 in tachimint and dashboard

Fixes critical SSRF and header injection vulnerabilities (CVE).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: upgrade golang.org/x/oauth2 to v0.36.0

Fixes high severity input validation vulnerability in oauth2.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: add Sepolia claim/mint MVP plan and tachimint boundary docs (#226)

- docs/tachimint-loyalty-claim-boundary.md: T-Point vs $TACHI 產品邊界說明
- plans/sepolia-claim-mint-mvp.md: Sepolia MVP 完整計畫
- plans/sepolia-claim-mint-issue-breakdown.md: 建議出票順序

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: fix dependabot-automerge to avoid "No jobs were run" notifications

Restructure job to always run one step (skip message) for non-Dependabot
PRs, preventing GitHub from sending workflow notification emails.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(deps-dev): bump the typescript-tooling group (#235)

Bumps the typescript-tooling group in /tachimint with 3 updates: [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js), [eslint](https://github.com/eslint/eslint) and [typescript](https://github.com/microsoft/TypeScript).


Updates `@eslint/js` from 9.39.4 to 10.0.1
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/commits/v10.0.1/packages/js)

Updates `eslint` from 9.39.4 to 10.2.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.4...v10.2.0)

Updates `typescript` from 5.8.3 to 6.0.2
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.8.3...v6.0.2)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 10.0.1
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: typescript-tooling
- dependency-name: eslint
  dependency-version: 10.2.0
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: typescript-tooling
- dependency-name: typescript
  dependency-version: 6.0.2
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: typescript-tooling
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump the react group in /tachimint with 2 updates (#236)

Bumps the react group in /tachimint with 2 updates: [react](https://github.com/facebook/react/tree/HEAD/packages/react) and [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom).


Updates `react` from 19.2.4 to 19.2.5
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.5/packages/react)

Updates `react-dom` from 19.2.4 to 19.2.5
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.2.5/packages/react-dom)

---
updated-dependencies:
- dependency-name: react
  dependency-version: 19.2.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: react
- dependency-name: react-dom
  dependency-version: 19.2.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: react
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump the deps group in /tachimint with 2 updates (#237)

Bumps the deps group in /tachimint with 2 updates: [globals](https://github.com/sindresorhus/globals) and [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint).


Updates `globals` from 17.4.0 to 17.5.0
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](https://github.com/sindresorhus/globals/compare/v17.4.0...v17.5.0)

Updates `typescript-eslint` from 8.58.1 to 8.58.2
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.58.2/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: globals
  dependency-version: 17.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: deps
- dependency-name: typescript-eslint
  dependency-version: 8.58.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: deps
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [chore] 移除 demo wallet binding helper (#243)

* chore: 移除 demo wallet binding helper

Demo 階段已結束(refs #208),此 CLI 為 demo-only 工具,
不再需要維護。

refs #242
closes #198

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* docs: restore TACHI_CONTRACT_ADDRESS and SEPOLIA_SIGNER_KEY in README

refs #242

---------

Co-authored-by: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* [discussion] Define tachimint Chrome sidepanel migration decision (#247)

* docs: define tachimint Chrome sidepanel migration decision

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

* docs: keep discussion PR out of tachimint surface

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

---------

Co-authored-by: Codex <codex[bot]@openai.com>

* [discussion] narrow auth architecture baseline (#215)

* docs: narrow auth architecture baseline

refs #58

refs #34

* docs: link auth baseline doc in readme

refs #58

refs #34

* [frontend] split dashboard streamers pages from auth migration (#216)

* feat: split streamers pages from auth migration

refs #69

* test: tighten streamers fallback handling

refs #69

* chore: drop dashboard lockfile from streamers pr

refs #69

* chore: add dashboard lockfile for vitest and jsdom

refs #217

* fix: remove synchronous setState calls in effects to satisfy lint

refs #217

* fix: address streamers fallback review comments

refs #69

Co-Authored-By: Codex <codex[bot]@openai.com>

---------

Co-authored-by: Erick52106 <fs63505200210@gmail.com>
Co-authored-by: Codex <codex[bot]@openai.com>

* [backend] 錢包綁定前置 — auth_providers partial unique index + SIWE helpers 抽取 (#254)

* docs: wallet binding spec — POST /users/me/wallet

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: fix wallet binding spec per review feedback

- SQLite 支援 partial index,test helper 應補同等 index
- step 6 補 RowsAffected == 1 防並發重放
- step 9 說明 step 8 soft delete 後 restore 邏輯
- lookupAddr 改用 strings.ToLower(checksumAddr)
- Create/restore 遇 unique violation 轉 ErrProviderLinked

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: add implementation checklist and migration 014 to wallet binding spec

- 補 migration 014 為物件清單第 0 項
- 拆 siwe.go 為 1a(新增)/ 1b(修改 auth_service.go)
- 補 implementation checklist 避免隱含工作被漏

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: final spec polish before implementation plan

- checklist 路徑補 backend/ 前綴
- auth_service.go unused import 精準化(go-ethereum/crypto)
- swagger_types.go 補 WalletResponse 定義
- router 編號更新為 5
- test cases 補 nonce replay 與 race → ErrProviderLinked

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: nit fixes — checklist swagger_types, remove HTTP 200 from service test

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: wallet binding implementation plan

5 tasks: migration, siwe extraction, UserService.LinkWallet (TDD),
UserHandler + swagger types, router. Full code in every step.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: migration 014 — partial unique index on auth_providers

Replaces UNIQUE(provider, provider_id) with a partial index scoped to
active rows (deleted_at IS NULL), enabling soft-delete + re-bind without
constraint violations. SQLite test helpers updated in both services and
handlers packages.

refs #249

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor: extract SIWE helpers to services/siwe.go

siweMessage and verifyEthSignature shared between auth_service and
upcoming user_service.LinkWallet. Removes go-ethereum/crypto import
from auth_service.go.

refs #249

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: remove LinkWallet route from handler test env

Handler not yet implemented in this PR (comes in #253).

refs #251

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: siweMessage 加 issuedAt 參數,修正驗證非決定性問題

使用 nonceRecord.CreatedAt 重建訊息,確保簽章與驗證
使用完全相同的字串,消除 time.Now() 造成的競態。

refs #251

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: nonce API 回傳 issued_at,修正客戶端無法重建 SIWE 訊息問題

POST /auth/web3/nonce 現在回傳 {"nonce":"...","issued_at":"..."},
客戶端用 issued_at 建構與伺服器相同的 EIP-4361 訊息後簽署。

refs #251

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: Web3Nonce test 補錯誤斷言,避免靜默失敗

refs #251

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* [backend] Agency Onboarding — partial success recovery flow (#244)

* feat: add AgencyService.GetByID with onboarding status

onboarding_complete is derived from password_hash IS NOT NULL,
no schema change required.

refs #146

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add GET /agencies/:id and POST /agencies/:id/resend-setup

GET returns profile + onboarding_complete (derived from password_hash).
POST resend-setup retriggers ForgotPassword; returns 500 on failure
so admin knows the resend did not succeed.

refs #146

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: wire GET /agencies/:id and POST /agencies/:id/resend-setup; improve log format

Log messages now use key=value format (agency_id=, email=, err=)
for grep-friendly observability.

refs #146

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: add agency onboarding recovery implementation plan

refs #146

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address code review — add onboarding_complete=true test and ForgotPassword comment

- Add TestAgencyHandler_Get_OnboardingComplete to cover handler-layer
  onboarding_complete=true path (previously only tested at service layer)
- Add comment in ResendSetup explaining ForgotPassword silent-nil assumption
  and when it would need to be revisited

refs #146

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address CodeRabbit review comments

- ResendSetup: nil-guard user.Email before dereference, extract local email var
- Add TestAgencyHandler_ResendSetup_MailerError and DBError (500 failure paths)
- Fix outdated comment referencing old POST /auth/forgot-password endpoint
- plans: mark agency-onboarding-recovery as Status: Completed

refs #146

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: add auth/role coverage for GET /agencies/:id and POST /agencies/:id/resend-setup

- TestAgencyHandler_Get_RequiresAuth (401)
- TestAgencyHandler_Get_ForbiddenRole (403 for non-admin/agency role)
- TestAgencyHandler_ResendSetup_RequiresAuth (401)

refs #146

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: propagate non-NotFound DB errors from ForgotPassword

Previously ForgotPassword swallowed all errors from the email lookup,
meaning a transient DB failure would still return nil and cause
POST /agencies/:id/resend-setup to reply 200 even though no token was
written and no email was sent.

Now only gorm.ErrRecordNotFound is swallowed (anti-enumeration); other
errors are propagated to the caller.

refs #146

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: block resend-setup for agencies that have completed onboarding

POST /agencies/:id/resend-setup now returns 409 Conflict if the agency
already has a password set, preventing an unsolicited password-reset
email from being sent to an already-onboarded agency.

refs #146

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* [backend] support refresh token cookies for auth refresh/logout (#220)

* feat: support auth refresh cookies

refs #217

* test: assert refresh cookie clearing

refs #217

* test: tighten auth cookie coverage and fix airdrop test seed

refs #217

Co-Authored-By: Codex <codex[bot]@openai.com>

* test: assert cleared refresh cookie remains httponly

refs #217

Co-Authored-By: Codex <codex[bot]@openai.com>

* fix: harden refresh cookie contract

* test: assert refresh cookie max age

---------

Co-authored-by: Erick52106 <fs63505200210@gmail.com>
Co-authored-by: Codex <codex[bot]@openai.com>

* [backend] 錢包綁定 service 層 — UserService.LinkWallet(SIWE 驗證 + soft delete 策略) (#256)

* feat: UserService.LinkWallet — SIWE-verified wallet binding

Allows an authenticated user to bind a MetaMask wallet. Implements:
- address validation and EIP-55 checksum normalization
- SIWE nonce + signature verification
- atomic nonce consumption with RowsAffected guard
- soft-delete of old provider + restore-or-insert pattern
- ErrProviderLinked on cross-user conflict or race
- TranslateError: true in DB config for consistent gorm.ErrDuplicatedKey

refs #249

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: TestLinkWallet 補齊 10 cases(含 nonce/signature/replay/restore)

refs #252

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* fix: LinkWallet nonce DB 錯誤分流、siweMessage 補 issuedAt 參數

- nonce 查詢非 RecordNotFound 錯誤直接往上拋,不再誤判為使用者輸入錯誤
- siweMessage 呼叫補第三個參數 issuedAt(nonceRecord.CreatedAt RFC3339)
  修正與簽名端訊息不一致導致的驗證失敗

refs #252

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* fix: seedWalletNonce 回傳 record,補 siweMessage issuedAt 參數(測試)

- seedWalletNonce 改回傳 *models.Web3Nonce,讓測試取得 CreatedAt
- 9 個 siweMessage 呼叫補第三個參數 issuedAt(nr.CreatedAt RFC3339)
  修正測試無法通過簽名驗證的問題

refs #252

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* test: 補 TestLinkWallet_RaceUniqueViolation(unique violation → ErrProviderLinked)

模擬並發寫入情境:goroutine 先替 userA 插入相同地址的 active AuthProvider,
再呼叫 LinkWallet(userB),驗證 gorm.ErrDuplicatedKey 正確轉譯為 ErrProviderLinked。

refs #252

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* fix: SIWE message 改用 checksummed address,符合 EIP-4361 規範

lookupAddr(小寫)只用於 nonce DB 查詢;
siweMessage 與 verifyEthSignature 改傳 checksumAddr,
確保與前端照 EIP-4361 簽名的格式一致。

refs #252

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* test: siweMessage 測試簽名改用 checksummed address

配合 production code 的 EIP-4361 修正,
測試中 siweMessage 第一個參數從 strings.ToLower(addr) 改為 addr。

refs #252

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* fix: Web3Nonce address 正規化改用 HexToAddress + ToLower

原本只做 strings.ToLower,無前綴地址會與 LinkWallet 的
HexToAddress().Hex()+ToLower 正規化結果不一致,
導致 nonce 查詢找不到 row。統一使用相同正規化邏輯。

refs #252

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* fix: nonce Delete 移到 transaction 外,確保消耗獨立提交

原本 nonce Delete 在 transaction 內,後續回傳 ErrProviderLinked
等錯誤時整個 tx 回滾,nonce 未被消耗,可重複使用。
改為在 transaction 前單獨刪除並確認 RowsAffected == 1,
transaction 只負責 provider 狀態變更。

refs #252

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* chore: exempt Dependabot PRs from size/scope checks in Scope police

go.sum updates routinely exceed the 1800-line diff limit, causing
Dependabot PRs to always fail the required check. Policy checks were
already skipped for Dependabot; this extends the exemption to the
size and surface-separation checks as well.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: add extensions/tachigo-demo-sidepanel to Dependabot config

Ensures future Dependabot PRs for this directory target develop
instead of main.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: add review workflow to CLAUDE.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [frontend] feat(tachimint): add Chrome sidepanel runtime skeleton (#264)

* feat(tachimint): add Chrome sidepanel runtime skeleton

Establishes the Chrome Extension (Manifest v3) runtime foundation for
tachimint, enabling it to load as a Chrome sidepanel without touching
any existing product logic (hooks, App.tsx, api.ts, services).

- Add public/manifest.json (Manifest v3, sidePanel + storage permissions)
- Add sidepanel.html as Chrome sidepanel entry point
- Add src/extension/background.ts: minimal service worker that enables
  openPanelOnActionClick behavior; audio synthesis deferred to PR 3
- Add src/extension/content.ts: empty placeholder content script
- Add src/extension/storage.ts: chrome.storage.local + localStorage fallback
- Add src/extension/types.ts: DemoState types ported from migration source
- Update vite.config.ts: multi-entry build; background.js and content.js
  output without hash (Chrome service worker requires stable filenames)
- Update tsconfig.app.json: add "chrome" to types for @types/chrome
- Update package.json: add @types/chrome ^0.1.40 devDependency

Refs: #246
Part of: docs/tachimint-chrome-sidepanel-migration.md Step 1 of 4

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address sidepanel storage review comments

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

* fix: address frontend review follow-up

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

* fix: fall back when chrome storage write fails

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

* fix(tachimint): remove duplicate sidepanel action handling

* fix(tachimint): align sidepanel host permissions

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

* test(tachimint): run extension tests in ci

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Codex <codex[bot]@openai.com>

* chore: remove extensions/tachigo-demo-sidepanel from Dependabot config

Directory removed in PR #264.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: 更新 AI 分工——加入 Gemini CLI 為第三個協作工具

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [infra] Skip backend integration for docs-only PRs (#274)

* ci: skip backend integration for docs-only PRs

refs #273

Co-Authored-By: Codex <codex[bot]@openai.com>

* ci: keep integration tests on scope bypass

refs #273

Co-Authored-By: Codex <codex[bot]@openai.com>

---------

Co-authored-by: Codex <codex[bot]@openai.com>

* [chore] Standardize agent review workflow (#272)

* docs: code review workflow refactored to use Gemini CLI

改用 Gemini CLI 執行代碼審查的步驟 4-5,替代多個 Haiku agents。
- 單一 Gemini agent 一次性審查和評分(5 個維度)
- 自動降級:Gemini 不可用時詢問改用 Haiku agents
- 符合 delegation 策略,減少 token 消耗

實作檔案:
- ~/.claude/scripts/code-review-with-gemini.sh (審查腳本)
- ~/.claude/scripts/code-review-gemini-score.sh (評分工具)
- code-review skill (步驟 4-5 改動)

詳見 docs/code-review-refactor.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: code review refactoring — production hardening & blocker fixes

修復初版實作的 3 個 blocker 與多個風險:

**Blockers:**
1. grep -oP → sed(macOS 兼容)
2. allowed-tools 補齊 git log / gh api / 脚本 / gemini / test / rm
3. Fallback 機制完整實現(marker file + skill step 4b)

**其他改進:**
- CLAUDE.md 動態獲取(gh api)
- git history 完整蒐集(git log)
- related PR comments 收集
- JSON 解析改用 sed

方案B 已生產可用。下次 code review 自動使用修復版本。

詳見 docs/code-review-refactor.md § Production Hardening

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: standardize agent review workflow

refs #271

Co-Authored-By: Codex <codex[bot]@openai.com>

* docs: fix 3 high-priority issues from Codex review

**High Priority Fixes:**

1. gh pr diff failure handling
   - Previously: silently converted to fake string, Gemini produced [] (false negative)
   - Now: fails loudly with return 1, skips review entirely
   - Prevents misleading audit results

2. Remove fake "related PR comments" claim
   - Was: get_related_pr_comments() only listed filenames, not actual comments
   - Now: deleted function, reduced dimensions from 5→4
   - Removes false context that could mislead Gemini

3. Use gh pr review instead of gh pr comment
   - Was: only gh pr comment (not a real GitHub review object)
   - Now: gh pr review --request-changes / --approve
   - Produces actual CHANGES_REQUESTED / APPROVED review state

Status: All 3 High issues fixed. Ready for Beta testing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: add issue matching delegation rule

refs #271

Co-Authored-By: Codex <codex[bot]@openai.com>

* docs: clarify review JSON contracts

refs #271

Co-Authored-By: Codex <codex[bot]@openai.com>

* docs: address review workflow docs feedback

refs #271

Co-Authored-By: Codex <codex[bot]@openai.com>

* docs: clarify gemini review dimensions

refs #271

Co-Authored-By: Codex <codex[bot]@openai.com>

* docs: clarify review fallback boundary

refs #271

Co-Authored-By: Codex <codex[bot]@openai.com>

* docs: document gemini cli delegation

refs #271

Co-Authored-By: Codex <codex[bot]@openai.com>

* docs: clarify Gemini delegation workflow with task taxonomy

新增 Gemini 專責任務表格,明確列出 6 大類型工作:
- 代碼掃描、文件生成、測試草稿、Log 分析、依賴審查、PR 初審

確保團隊不浪費 Claude token 做可由 Gemini 勝任的重複性工作。

refs #GEMINI-WORKFLOW

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* docs: address PR review feedback on delegation rules and documentation

- Update PR title from [discussion] to [docs]
- Add cross-reference from AGENTS.md to .claude/rules/delegation.md as canonical source
- Remove user-specific paths from code-review-refactor.md and add scope disclaimer
- Document .claude/rules/ directory convention in CLAUDE.md
- Mark dual JSON schema format as TODO for future unification

refs #272

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* docs: clarify PR review instruction format and remove session metadata

- Add 下指令格式 section to CLAUDE.md: brief format for CodeRabbit/Codex comment URLs to avoid wasting tokens on lookups
- Remove session context metadata block from AGENTS.md (was appended by Codex session)

refs #272

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* docs: add PR review instruction format guidelines

- Add .claude/rules/pr-review-instructions.md: standard format for CodeRabbit/Codex comment URLs
- Explains why brief context avoids wasting tokens on lookups

refs #272

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Codex <codex[bot]@openai.com>

* docs: add granularity principles for issue/commit/PR

- Add section on atomic commits, single-responsibility issues, and focused PRs
- Explains benefits: better review, traceability, rollback, and faster cycles

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* [infra] Fix backend-integration condition and remove ineffective Go cache (#277)

* docs: code review workflow refactored to use Gemini CLI

改用 Gemini CLI 執行代碼審查的步驟 4-5,替代多個 Haiku agents。
- 單一 Gemini agent 一次性審查和評分(5 個維度)
- 自動降級:Gemini 不可用時詢問改用 Haiku agents
- 符合 delegation 策略,減少 token 消耗

實作檔案:
- ~/.claude/scripts/code-review-with-gemini.sh (審查腳本)
- ~/.claude/scripts/code-review-gemini-score.sh (評分工具)
- code-review skill (步驟 4-5 改動)

詳見 docs/code-review-refactor.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: code review refactoring — production hardening & blocker fixes

修復初版實作的 3 個 blocker 與多個風險:

**Blockers:**
1. grep -oP → sed(macOS 兼容)
2. allowed-tools 補齊 git log / gh api / 脚本 / gemini / test / rm
3. Fallback 機制完整實現(marker file + skill step 4b)

**其他改進:**
- CLAUDE.md 動態獲取(gh api)
- git history 完整蒐集(git log)
- related PR comments 收集
- JSON 解析改用 sed

方案B 已生產可用。下次 code review 自動使用修復版本。

詳見 docs/code-review-refactor.md § Production Hardening

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: optimize backend CI with caching and parallelization

實作四項優化:
1) Docker layer cache — 啟用 DOCKER_BUILDKIT
2) Go modules cache — actions/setup-go with cache: true
3) backend-integration 並行化 — needs 改為 [scope-gate]
4) integration tests 條件執行 — 只在 backend/ 檔案變更時跑

refs #276

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* ci: fix backend-integration condition and remove ineffective Go cache

修復之前改動的三個問題:

1) 恢復 scope-gate 輸出 run_backend_integration(計算檔案路徑)
   - 在所有早退路徑都明確設定此輸出
   - push 事件、scope-exception、一般 PR 都輸出正確值

2) backend-integration 改用 scope-gate 的輸出而非無效 contains()
   - 修復 GitHub Actions 表達式無法實際取得檔案列表的問題
   - 恢復 backend/ contracts/ docker-compose* .github/workflows/ci.yml 路徑檢查

3) 移除 setup-go(Docker-only job 中無效)
   - Go cache 在 container 內無效,Docker build 時 go mod 仍會重新下載
   - 保留 DOCKER_BUILDKIT=1 以啟用 Docker layer cache

4) 恢復 backend-integration 依賴 backend job
   - 確保單元測試先執行,整合測試才能跑
   - 符合 branch protection required checks 預期

refs #276

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* ci: add DOCKER_BUILDKIT to backend-integration build step

Fix CodeRabbit feedback:
1. Add DOCKER_BUILDKIT=1 env to backend-integration Build image step (parity with backend job)
2. Replace absolute paths with relative paths in code-review-refactor.md

refs #276

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* docs: add language tags to fenced code blocks (MD040)

- Line 52, 97, 226: Add `text` language tag to unlabeled code blocks
- Add blank lines before code blocks per MD031 requirements

refs #277

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* docs: add granularity principles for issue/commit/PR

- Add section on atomic commits, single-responsibility issues, and focused PRs
- Explains benefits: better review, traceability, rollback, and faster cycles

refs #272

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* docs: remove AI division section (out of PR #277 scope)

This section declares Gemini CLI usage, which is out of scope for this PR
that focuses on CI optimization without introducing new tools.

Commit this tool-related decision to a separate PR or docs.

refs #277

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* docs: remove granularity principles section (out of PR #277 scope)

This section is a documentation policy update unrelated to CI optimization.
Per CLAUDE.md scope rules, documentation changes should be in separate PRs.

Move this to docs/refine-granularity-guidelines PR instead.

refs #277

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Codex <codex[bot]@openai.com>

* [discussion] Standardize Gemini-assisted PR review flow (#278)

* docs: standardize gemini pr review flow

refs #271

Co-Authored-By: Codex <codex[bot]@openai.com>

* docs: avoid heavy git fetches in PR review

refs #271

Co-Authored-By: Codex <codex[bot]@openai.com>

* docs: prefer flash for gemini delegation

refs #271

Co-Authored-By: Codex <codex[bot]@openai.com>

---------

Co-authored-by: Codex <codex[bot]@openai.com>
Co-authored-by: Erick52106 <fs63505200210@gmail.com>

* [infra] add Git LFS tracking for tachimint assets (#280)

* chore: add Git LFS tracking for tachimint assets

refs #279

Co-Authored-By: Codex <codex[bot]@openai.com>

* chore: normalize tachimint asset pointer

refs #279

Co-Authored-By: Codex <codex[bot]@openai.com>

* docs: align LFS migrate asset patterns

refs #279

Co-Authored-By: Codex <codex[bot]@openai.com>

---------

Co-authored-by: Codex <codex[bot]@openai.com>

* docs: finalize PR review guidelines and agent responsibilities

- Add comprehensive PR review process with clear Gemini/Claude/Codex separation
- Document 3 review execution modes (standard/minimal/high-risk)
- Add structured output format (Summary/Blockers/Majors/Minors/Nits)
- Define interaction prompts for 4 decision scenarios
- Document Codex review guidelines to minimize token usage
- Update AGENTS.md with parallel operation rules and workflow details

refs #265

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* ci: add close reason comment to auto-closed PRs

When scope-police auto-closes a PR, include a detailed comment listing
the scope violations before the close event. This way users see the
reason immediately without needing to find comments in the timeline.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* ci: raise PR diff hard limit from 1800 to 2500 lines

Version upgrades with auto-generated diffs (Swagger, etc.) often exceed
1800 lines when combined with related changes. 2500 lines still maintains
reasonable reviewability while accommodating legitimate refactors.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* ci: implement tiered diff limits with warning threshold

- hardMaxDiffLines: 1500 (blocks standard PRs, except release)
- warningDiffLines: 800 (warns, does not block)
- Release promotion PRs exempt from diff limits
- scope-exception label for legitimate exceptions:
  migration, generated code, dependency bumps

Rationale: AI review token overhead increases sharply beyond 800 lines.
Hard limit at 1500 protects code quality while allowing necessary large PRs.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* ci: limit exception PRs to 2500 lines, release PRs unlimited

scope-exception label allows up to 2500 lines for legitimate exceptions
(migration, generated code, dependency bumps). Only release promotion
PRs (develop -> main) can exceed 2500 lines.

This prevents scope-exception from becoming a loophole for giant PRs.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* docs: add PR size guideline with performance bands

Include performance metrics from ChatGPT analysis:
- < 200 lines: optimal (instant review)
- 200-400 lines: good (standard review)
- 400-800 lines: acceptable but needs segmented review (token overhead)
- 800-1500 lines: risk zone (recommend split)
- 1500+ lines: hard block (exceptions only)

This guides authors toward performant PRs while enforcing hard limits.
Resolve merge conflict in AGENTS.md.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* [chore] Swagger 契約正規化與產生器版本對齊(移除 invalid "": []) (#275)

* chore: normalize swagger security output and align swag version

refs #270

Co-Authored-By: Codex <codex[bot]@openai.com>

* test: remove redundant t.Helper calls in swagger tests

refs #270

Co-Authored-By: Codex <codex[bot]@openai.com>

* fix: normalize swagger security contract for public endpoints

refs #270

Co-Authored-By: Codex <codex[bot]@openai.com>

---------

Co-authored-by: Codex <codex[bot]@openai.com>

* docs: clarify read-only operations don't require user confirmation

- `gh pr view`, `gh pr list`, `gh issue view`, `gh api` (read requests) can execute directly
- State-changing operations (comment, review, merge, etc) still require explicit user consent

refs #275

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* ci: exempt pure docs/template/metadata PRs from backend contract checks

為純文檔改動提供豁免機制:

- 若 PR 只改動 docs/、plans/、.github/ISSUE_TEMPLATE/、.github/PULL_REQUEST_TEMPLATE.md、
  README.md、.gitignore、.gitattributes 等非產品文件,自動豁免 backend contract 聲明要求
- 改進正則表達式以支持更靈活的格式(前置 dash、多行匹配、全角冒號等)
- 在 scope police comment 中顯示 "Docs/template/metadata only" 模式指示

好處:
- 防止文檔改動被過度約束
- 保留 scope pollution 防護機制
- 改善團隊 DX(不需要每次加 label)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* [frontend] migrate demo app-shell foundations into tachimint (1/4) (#265)

* fix: address frontend review follow-up

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

* feat(tachimint): add assets, styles, i18n and coupon catalog for app shell

- Add logo PNG and pixel fonts (PressStart2P, Zpix)
- Add src/styles/fonts.css and src/styles/index.css (pixel UI theme)
- Import new stylesheets in src/index.css
- Add src/extension/couponCatalog.ts with COUPON_METAS constant
- Extend i18n locales (en / zh-TW / zh-CN) with loading, login, hud,
  nonViewer, coupon and error keys required by the new app shell;
  preserve all existing claim.actions / claim.errors / contextLoading keys
- Simplify storage.ts: remove chrome→localStorage fallback path (chrome
  storage.set no longer throws); remove corresponding test case

refs #246

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: stop reviving legacy sidepanel demo state

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

* fix: address coderabbit review comments

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

* fix: tighten i18n parity token checks

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

* fix: address tachimint app shell review regressions

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

* fix: tighten tachimint CI and font fallbacks

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

* fix: make workflow check CI-safe in frontend tests

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

* fix: address PR 265 review follow-ups

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

* fix: mirror localStorage on Chrome write success to prevent state rollback

After a successful Chrome storage write, saveDemoState() now keeps
localStorage in sync as a fallback mirror instead of clearing it.
This ensures that if Chrome storage read later fails, loadDemoState()
can still recover the most recent state from localStorage.

Also updates docs/tachimint-chrome-sidepanel-migration.md with
implementation slice details (#265–#268) so the doc can serve as
the concrete source of truth for this PR.

refs #246

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: move app shell assets to git lfs

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

---------

Co-authored-by: Codex <codex[bot]@openai.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* [backend] 錢包綁定 API — POST /users/me/wallet(已登入 Twitch 使用者綁定 MetaMask) (#269)

* docs: swagger — POST /users/me/wallet(WalletResponse + LinkWalletInput)

refs #253

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* feat: UserHandler.LinkWallet — POST /users/me/wallet

Adds WalletResponse swagger type, LinkWallet handler(400/401/409/500
error mapping),testutil_test.go wallet route,以及 router 路由。

refs #253

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* test: handler tests for LinkWallet(6 cases)

補齊 6 handler test cases:NoAuth / InvalidAddress / InvalidNonce /
InvalidSignature / Success / WalletAlreadyLinked。
各 error case 驗證 HTTP status 及 error message。
handlerSIWEMessage 使用 checksummed address 與 nonce.CreatedAt 作為
issuedAt,與 service 邏輯對齊。

closes #253

Co-Authored-By: Claude Sonnet 4.6 <claude[bot]@anthropic.com>

* docs: align LinkWallet 500 response in swagger

refs #253

Co-Authored-By: Codex <codex[bot]@openai.com>

* refactor: align LinkWallet response with WalletResponse swagger type

refs #253

Co-Authored-By: Codex <codex[bot]@openai.com>

---------

Co-authored-by: Claude Sonnet 4.6 <claude[bot]@anthropic.com>
Co-authored-by: Codex <codex[bot]@openai.com>

* [chore] 新增任務拆細規則與 GitHub issue/PR template 更新 (#283)

* docs: clarify read-only operations don't require user confirmation

- `gh pr view`, `gh pr list`, `gh issue view`, `gh api` (read requests) can execute directly
- State-changing operations (comment, review, merge, etc) still require explicit user consent

refs #275

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* chore: add task/PR splitting rules to AGENTS.md, CLAUDE.md, and GitHub templates

- AGENTS.md: 新增 Task / PR 拆分規則(第 120-191 行)
- CLAUDE.md: 新增「何時應該拆細」checklist 和「Claude Code 實作前必檢」
- GitHub templates: 在 PR template、feature_request、bug_report 中新增 scope/non-goals/acceptance criteria/suggested PR split 字段

這次改動讓 Claude Code、Codex 和團隊成員在 issue、PR、實作前都會看到同一套協議。

refs #275

Co-Authored-By: Codex <codex[bot]@openai.com>

* chore: add .continue/ to .gitignore

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* docs: add Interface/Reference fields to feature and bug templates

補充「介面/規格」和「參考」欄位,避免需求落地時規格斷層。

Based on CodeRabbit feedback on PR #283:
- feature_request.md: 新增「介面/規格」(Go interface、API 規格、React props 等)
- feature_request.md: 新增「參考」(現有實作、範本路徑、設計文件)
- bug_report.md: 新增「相關代碼或介面」(受影響的函數、API、component)
- bug_report.md: 新增「參考」(相關 issue、commit、錯誤日誌位置)

這兩個欄位可防止實作時出現規格解讀差異,並加速定位現有參考實作。

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Co-authored-by: Codex <codex[bot]@openai.com>

* [frontend] add LoginScreen, LoadingScreen, LanguageSwitcher, useSound and theme to tachimint (2b/5) (#266)

* feat(tachimint): add LoginScreen, LoadingScreen, LanguageSwitcher, useSound and theme

- LoginScreen: pixel-art login form with username/password, i18n, error state
- LoadingScreen: animated progress bar with logo; fires onComplete callback
- LanguageSwitcher: compact flag-button toggle (EN / 繁 / 简)
- useSound: Web Audio API hook (coin collect, claim, redeem, BGM loop)
- theme/backgrounds.ts: shared gradient/panel background constants

refs #246

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address coderabbit feedback

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Codex <codex[bot]@openai.com>

* [infra] Cache backend Docker build layers in CI (#281)

* ci: cache backend Docker builds

refs #276

Co-Authored-By: Codex <codex[bot]@openai.com>

* ci: execute backend CI cache wiring check in workflow

The check-backend-ci-cache.sh script verifies Docker Buildx configuration and
cache semantics but was not invoked by any automation. Add early-stage check
job to prevent regressions in cache setup.

Fixes: https://github.com/nurockplayer/tachigo/pull/281#discussion_r3105494502

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* docs: update Gemini --yolo usage to default-off

Change Gemini usage recommendation from always using --yolo to conditional.
Default to regular execution with user confirmation prompts, use --yolo only
when explicitly confirmed safe to avoid accidental failures.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* [infra] CI — remove redundant cache-to from backend-integration job

backend-integration depends on backend which already writes to GHA cache.
Removing cache-to avoids redundant cache write overhead and saves CI time.

refs #285

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* Revert "docs: update Gemini --yolo usage to default-off"

This reverts commit 60b6f807bc461fe162d9b7eeb3a2f6228fed680f.

---------

Co-authored-by: Codex <codex[bot]@openai.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>

* feat: add tachimint core interactive components

refs #246

Co-Authored-By: Codex <codex[bot]@openai.com>

* [infra] CI — parallelize backend unit and integration tests (#288)

* [infra] CI — extract backend-build job and parallelize tests

Create independent backend-build job that builds image once, then backend
and backend-integration jobs download the artifact and run tests in parallel.

This reduces total CI time from (build + unit + integration) to
(build + max(unit, integration)).

Changes:
- New backend-build job: build → docker save → upload artifact
- backend job: download artifact → docker load → run unit tests
- backend-integration job: download artifact → docker load → run integration tests
- Both test jobs now depend on backend-build (parallel execution)
- notify-discord updated to include backend-build in needs

refs #285

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* fix: add pipefail protection to docker save export step

Ensure docker save command failure fails fast, preventing upload of
corrupted artifacts. Add explicit file size validation.

refs #285

* fix: add backend-ci gate job to enforce build failure checks

Ensure backend image build failures are reported as CI failures, not
skipped jobs, so PR merge is properly blocked by required checks.

The backend-ci gate depends on backend-build, backend, and
backend-integration. If backend-build fails, the gate fails, guaranteeing
required checks will block merge.

Update pr-scope-policy.md to reflect the new gate check name.

refs #285

* fix: backend-ci gate should allow scope-based skipped jobs

Only fail when backend-build, backend, or backend-integration actually
fail. Allow them to be skipped due to scope-gate rules.

refs #285

* fix: backend-ci gate should reject cancelled status

Use allowlist mode: only success or skipped are acceptable.
Prevents branch protection bypass when jobs are manually cancelled.

refs #285

* fix: use proper GitHub Actions env for job result checks

Cannot dynamically expand needs.$job.result via expressions.
Use env variables to pass values, then iterate in shell.

Fixes: https://github.com/nurockplayer/tachigo/pull/288#discussion_r3106991665

refs #285

* ci: trigger scope-gate re-evaluation with ASCII colon fix

---------

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>

* [infra] Backend integration tests — testcontainers 取代 docker-compose postgres (#293)

* [infra] CI — extract backend-build job and parallelize tests

Create independent backend-build job that builds image once, then backend
and backend-integration jobs download the artifact and run tests in parallel.

This reduces total CI time from (build + unit + integration) to
(build + max(unit, integration)).

Changes:
- New backend-build job: build → docker save → upload artifact
- backend job: download artifact → docker load → run unit tests
- backend-integration job: download artifact → docker load → run integration tests
- Both test jobs now depend on backend-build (parallel execution)
- notify-discord updated to include backend-build in needs

refs #285

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* fix: add pipefail protection to docker save export step

Ensure docker save command failure fails fast, preventing upload of
corrupted artifacts. Add explicit file size validation.

refs #285

* fix: add backend-ci gate job to enforce build failure checks

Ensure backend image build failures are reported as CI failures, not
skipped jobs, so PR merge is properly blocked by required checks.

The backend-ci gate depends on backend-build, backend, and
backend-integration. If backend-build fails, the gate fails, guaranteeing
required checks will block merge.

Update pr-scope-policy.md to reflect the new gate check name.

refs #285

* fix: backend-ci gate should allow scope-based skipped jobs

Only fail when backend-build, backend, or backend-integration actually
fail. Allow them to be skipped due to scope-gate rules.

refs #285

* fix: backend-ci gate should reject cancelled status

Use allowlist mode: only success or skipped are acceptable.
Prevents branch protection bypass when jobs are manually cancelled.

refs #285

* fix: use proper GitHub Actions env for job result checks

Cannot dynamically expand needs.$job.result via expressions.
Use env variables to pass values, then iterate in shell.

Fixes: https://github.com/nurockplayer/tachigo/pull/288#discussion_r3106991665

refs #285

* ci: trigger…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-codex-review New commits pushed after CHANGES_REQUESTED — pending Codex review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant